# ifndef SCREEN_H
# define SCREEN_H
#include <iostream>
#include <vector>
#include "WindowMgr.h"
using std::cerr, std::clog;
using std::cin, std::cout, std::endl;

/**
 * @brief Screen表示显示器中的一个窗口。
 * 每个Screen包含一个用于保存Screen内容的string成员和三个string：：size_type类型的成员，
 * 它们分别表示光标的位置以及屏幕的高和宽。
 */
class Screen
{
    // 声明友元类
    friend class Window_mgr; // Window_mgr的成员可以访问Screen类的私有部分
    // 令成员函数作为友元
    // friend void Window_mgr::clear(ScreenIndex);
public:
    typedef std::string::size_type pos; // 用来定义类型的成员必须先定义后使用，这一点与普通成员有所区别，
    // 具体原因将在7.4.1节（第254页）解释。因此，类型成员通常出现在类开始的地方。
    // using pos = std::string::size_type; // 等价的声明pos
    Screen() = default;       // 因为我们已经提供了一个构造函数，所以编译器将不会自动生成默认的构造函数。
                              // 如果我们的类需要默认构造函数，必须显式地把它声明出来。
    void some_member() const; // 常量成员函数 const成员函数
    // 设置光标所在位置的字符或者其他任一给定位置的字符
    Screen &set(char);
    Screen &set(pos, pos, char); // 返回*this的成员函数
    // 根据对象是否是const重载了display函数
    // 当do_display完成后，display函数各自返回解引用this所得的对象。
    // 在非常量版本中，this指向一个非常量对象，因此display返回一个普通的（非常量）引用；而const成员则返回一个常量引用
    Screen &display(std::ostream &os)
    {
        do_display(os);
        return *this; // 返回一个普通的Screen的引用
    }
    const Screen &display(std::ostream &os) const
    {
        do_display(os);
        return *this; // 返回一个常量的Screen的引用
    }

    // cursor被其类内初始值初始化为0
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} // 构造函数
    char get() const { return contents[cursor]; }                                   // 隐式内联 读取光标处的字符
    inline char get(pos ht, pos wd) const;                                          // 显示内联
    Screen &move(pos r, pos c);                                                     // 能在之后被设为内联

private:
    mutable size_t access_crt; // 即使在const成员函数内也能被修改
    // 该函数负责显示Screen的内容 
    // 建议：对于公共代码使用私有功能函数
    void do_display(std::ostream &os) const
    {
        os << contents;
    }

private:
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;
};

// 我们可以在类的内部把inline作为声明的一部分显式地声明成员函数，
// 同样的，也能在类的外部用inline关键字修饰函数的定义：
// 虽然我们无须在声明和定义的地方同时说明inline，但这么做其实是合法的。
// 不过，最好只在类外部定义的地方说明inline，这样可以使类更容易理解。
inline Screen &Screen::move(pos r, pos c)
{
    pos row = r * width; // 计算行的位置
    cursor = row + c;    // 在行内将光标移动到指定的列
    return *this;        // 以左值的形式返回对象
}
char Screen::get(pos r, pos c) const
{
    pos row = r * width;      // 计算行的位置
    return contents[row + c]; // 返回给定列的字符
}

void Screen::some_member() const
{
    // 尽管some_member是一个const成员函数，但由于access_ctr是可变数据成员，所以some_member仍然能够改变的access_ctr值。
    ++access_crt; // 保存一个计数值，用于记录成员函数被调用的次数
}

// 返回引用的函数是左值的（参见6.3.2节，第202页），意味着这些函数返回的是对象本身而非对象的副本。
inline Screen &Screen::set(char c)
{
    contents[cursor] = c; // 设置当前光标所在位置的新值
    return *this;         // 将this对象作为左值返回
}

inline Screen &Screen::set(pos r, pos c, char ch)
{
    contents[r * width + c] = ch; // 设置给定位置的新值
    return *this;                 // 将this对象作为左值返回
}

// 一个const成员函数如果以引用的形式返回＊this，那么它的返回类型将是常量引用
// inline const Screen &Screen::display(std::ostream &os) const
// {
//     os << contents;
//     return *this;
// }

#endif